﻿/*
 * Copyright 2010 Pieter De Rycke
 *
 * This file is part of Frozen Bubble XNA.
 *
 * Frozen Bubble XNA is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2, as published by the Free Software Foundation.
 *
 * Frozen Bubble XNA is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with Frozen Bubble XNA. If not, see 
 * <http://www.gnu.org/licenses/>.
 *
 * Artwork:
 *    Alexis Younes <73lab at free.fr>
 *      (everything but the bubbles)
 *    Amaury Amblard-Ladurantie <amaury at linuxfr.org>
 *      (the bubbles)
 *
 * Soundtrack:
 *    Matthias Le Bidan <matthias.le_bidan at caramail.com>
 *      (the three musics and all the sound effects)
 *
 * Design & Programming:
 *    Guillaume Cottenceau <guillaume.cottenceau at free.fr>
 *      (design and manage the project, whole Perl sourcecode)
 *
 * XNA and Windows Phone 7 port:
 *    Pieter De Rycke
 *
 * Websites:
 *    http://www.frozen-bubble.org/
 *    http://pieterderycke.wordpress.com/
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace FrozenBubble
{
    public class Bubble
    {
        private IPlayer _player;

        public List<Bubble> NeighBours { get; set; }
        public Vector2 ScreenPosition { get; set; }
        public Position ArrayPosition { get; set; }
        public double Direction { get; set; }
        public int Number { get; private set; }
		public float SpeedX { get; set; }
		public float SpeedY { get; set; }
        public int Mark { get; set; }
        public int WaitFall { get; set; }
        public float Speed { get; set; }
        public int ChainDestx { get; set; }
        public int StickingStep { get; set; }
        public bool StickingSlowdown { get; set; }
        public bool IsRootBubble { get; set; }

        public Bubble(IPlayer player, Position arrayPosition, Vector2 screenPosition, int number)
        {
            _player = player;

            ArrayPosition = arrayPosition;
            ScreenPosition = screenPosition;
            Number = number;
            NeighBours = new List<Bubble>();
            StickingStep = -1;
            StickingSlowdown = false;
        }

        public Bubble(IPlayer player, int arrayPosX, int arrayPosY, Vector2 screenPosition, int number)
            : this(player, new Position(arrayPosX, arrayPosY), screenPosition, number)
        {
        }

        public bool NextTo(Bubble bubble)
        {
            return (Common.Sqr(ArrayPosition.X + Common.Odd(ArrayPosition.Y + BubbleConstants.ToInt(_player.OddSwap)) * 0.5 - (bubble.ArrayPosition.X + Common.Odd(bubble.ArrayPosition.Y + BubbleConstants.ToInt(_player.OddSwap)) * 0.5))
                + Common.Sqr(ArrayPosition.Y - bubble.ArrayPosition.Y)) < 3.0;
        }

        public bool IsCollision(Bubble bubble)
        {
            double distanceCollisionSqred = Common.Sqr(BubbleConstants.BUBBLE_SIZE * 0.82);
            double xs = Common.Sqr(ScreenPosition.X - bubble.ScreenPosition.X);
            if (xs > distanceCollisionSqred)
                return false;
            else
                return (xs + Common.Sqr(ScreenPosition.Y - bubble.ScreenPosition.Y)) < distanceCollisionSqred;
        }

        public void FindClosestArrayPosition()
        {
            FindClosestArrayPosition(ScreenPosition.X, ScreenPosition.Y);
        }

        public void FindClosestArrayPosition(float x, float y)
        {
            int arrayY = (int)((y - _player.TopLimit + BubbleConstants.ROW_SIZE / 2) / BubbleConstants.ROW_SIZE);
            int arrayX = (int)((x - _player.LeftLimit + BubbleConstants.BUBBLE_SIZE / 2 
                - BubbleConstants.Odd(arrayY + BubbleConstants.ToInt(_player.OddSwap)) 
                * BubbleConstants.BUBBLE_SIZE / 2) / BubbleConstants.BUBBLE_SIZE);

            ArrayPosition = new Position(arrayX, arrayY);
        }

        public HashSet<Bubble> FindBubbelGroup()
        {
            FindNeighbours();

            HashSet<Bubble> group = new HashSet<Bubble>();

            List<Bubble> newFounds = new List<Bubble>();
            newFounds.Add(this);
            group.Add(this);

            while (newFounds.Count > 0)
            {
                List<Bubble> previouslyFounds = new List<Bubble>(newFounds);
                newFounds.Clear();
                foreach (Bubble bubble in previouslyFounds)
                {
                    foreach (Bubble neighbour in bubble.NeighBours)
                    {
                        if (neighbour.Number == bubble.Number && !group.Contains(neighbour))
                        {
                            group.Add(neighbour);
                            newFounds.Add(neighbour);
                        }
                    }
                }
            }

            return group;
        }

        public void FindNeighbours()
        {
            NeighBours.Clear();

            foreach (Bubble bubble in _player.StickedBubbles)
            { 
                if(NextTo(bubble))
                {
                    NeighBours.Add(bubble);
                }
            }
        }
    }

    public struct Position
    {
        public int X;
        public int Y;

        public Position(int x, int y)
        {
            X = x;
            Y = y;
        }
    }
}
